/*
   tests in:
   - ChatterUserTriggerHelperTest.cls
   
   being used in:
   - User_CycleTeamSync.trigger
   
   JIRA
   - AD-26665: add/remove OOR users in OOR Chatter groups
   - AD-29656: add/remove all users in AT&T groups
*/
public class ChatterUserTriggerHelper 
{	
	public static final Map<String,String> OOR_RVP_CHATTER_GROUPS_MAP = new Map<String,String>
	{
		'OOR Mid Atlantic and Mountain West RVP' => 'OOR Sales – Scott Gottlieb\'s Region',
		'OOR East and Northwest RVP' => 'OOR Sales - Pat Eastburn\'s Region'
	};
	
	public static final Map<String,String> OOR_DIR_CHATTER_GROUPS_MAP = new Map<String,String>
	{
		'OOR East Region Director' => 'OOR Sales – East Region',
		'OOR Northwest Region Director' => 'OOR Sales Northwest Region',
		'OOR Mid Atlantic Region Director' => 'OOR Sales – Mid Atlantic Region',
	   'OOR Mountain West Region Director' => 'OOR Sales – Mountain West Region'
	};
	
	public static final Set<String> OOR_SALES_CHATTER_GROUPS = new Set<String>
	{
		'OOR Sales'
	};
	
	public static final Set<String> ALL_USER_CHATTER_GROUPS = new Set<String>
	{
		'All AT&T'
	};
	
	public static final Set<String> OOR_CHATTER_DISTRO_PROFILES = new Set<String>
	{
		'SFA OOR GSM and DSM', 'SFA OOR Media Consultants', 'SFA OOR Sales Director and VP'
	};
	
	// get ALL active users restrict by the limit, need it for managers since there is no pattern on who the manager would be
	public static final Map<Id,User> ALLUSERSMAP = new Map<Id,User>(
	   [SELECT Id, UserRole.Name, ProfileId, Profile.Name, ManagerId, Manager.Id, Manager.UserRole.Name FROM User 
	    WHERE IsActive = true LIMIT :Limits.getLimitDmlRows()]);
	
	@future
	public static void AsyncAddUsersToChatterGroups(Set<Id> uIds)
	{
		//if(!Test.isRunningTest())
		   AddUsersToChatterGroups(uIds);
	}
	
	public static void AddUsersToChatterGroups(Set<Id> uIds)
	{
		List<User> users = getUsers(uIds).values();
		
	   Set<String> allGroupsNames = new Set<String>();
	   allGroupsNames.addAll(OOR_RVP_CHATTER_GROUPS_MAP.values());
	   allGroupsNames.addAll(OOR_DIR_CHATTER_GROUPS_MAP.values());
		allGroupsNames.addAll(OOR_SALES_CHATTER_GROUPS);
		allGroupsNames.addAll(ALL_USER_CHATTER_GROUPS);
		
		Map<String, CollaborationGroup> allGroupsMap = getAllGroupNameMap(allGroupsNames, users);
		Map<Id,Set<String>> useGroupNameMap = new Map<Id,Set<String>>();

		for(User u : users)
		{
			if(!useGroupNameMap.containskey(u.Id))
			   useGroupNameMap.put(u.Id, new Set<String>());
			   
			useGroupNameMap.get(u.Id).addAll(calcUserChatterGroups(u, allGroupsMap));
		}
		
		//removeExisitngMemberships(useGroupNameMap.keyset(), allGroupsNames);
		//addUserToGroup(useGroupNameMap, allGroupsMap);
		calcUserMemberships(useGroupNameMap, allGroupsMap);
	}
	
	/*
	   get a map of the users specified in the set
	*/
	private static Map<Id,User> getUsers(Set<Id> uIds)
	{
		// requery to get look up information, can't use the static all user map for insert since it's initiated before the record
		// is committed
      Map<Id,User> currentUsers = new Map<Id,User>(
         [SELECT Id, ProfileId, Profile.Name, ManagerId, Manager.Id, Manager.UserRoleId, Manager.UserRole.Name, UserRole.Name
          FROM User
          WHERE Id IN :uIds AND IsActive = true]);
      /*Map<Id,User> currentUsers = new Map<Id,User>();
      for(Id uId : uIds)
      {
      	if(AllUsersMap.containskey(uId))
      	{
      		currentUsers.put(uId,AllUsersMap.get(uId));
      	}
      }*/
          
      return currentUsers;  
	}
	
	/*
	   get a map of group records by the group names specified
	*/
	private static Map<String,CollaborationGroup> getAllGroupNameMap(Set<String> allGroupsNames, List<User> uList)
	{
		Set<Id> managerIds = new Set<Id>();
		for(User u : uList)
		{
			managerIds.add(u.ManagerId);
		}
		Map<String, CollaborationGroup> allGroupsMap = new Map<String, CollaborationGroup>();
		Map<Id,CollaborationGroup> allGroupsIdMap = new Map<Id,CollaborationGroup>(
		   [SELECT Id, Name, OwnerId,
		   (SELECT Id, CollaborationGroupId, MemberId 
		    FROM GroupMembers 
		    WHERE MemberId IN :uList OR MemberId IN :managerIds)
		    FROM CollaborationGroup 
		    WHERE Name IN :allGroupsNames]);
		    
	   for(CollaborationGroup g : allGroupsIdMap.values())
		{
			allGroupsMap.put(g.Name,g);
		} 
		return allGroupsMap;
	}
	
	/*
      add users to the appropriate groups
	*/
	private static Set<String> calcUserChatterGroups(User u, Map<String, CollaborationGroup> allGroupsMap)
	{
		Set<String> chatterGroups = new Set<String>();
		
		// check if user is OOR, if it is add to OOR groups
      if(OOR_CHATTER_DISTRO_PROFILES.contains(u.Profile.Name))
      {
      	chatterGroups.addAll(calcOORUserChatterGroups(u, allGroupsMap));
      }
   
      /*
	      AD-29656: add/remove all users in AT&T groups
	   
	      add all users to the public All AT&T group
	   */
      chatterGroups.addAll(ALL_USER_CHATTER_GROUPS);
      
      return chatterGroups;
	}
	
	/*
	   AD-26665: add/remove OOR users in OOR Chatter groups
	   
	   If new user is added in OOR, the user should be added to the General OOR Chatter Group (ALL) and 
	   to the Manager's Chatter Group
	*/
	private static Set<String> calcOORUserChatterGroups(User u, Map<String, CollaborationGroup> allGroupsMap)
	{
		Set<String> chatterGroups = new set<String>();	
		
		Map<String,String> allOORManagerGroupMap = new Map<String,String>();
	   allOORManagerGroupMap.putAll(OOR_RVP_CHATTER_GROUPS_MAP);
	   allOORManagerGroupMap.putAll(OOR_DIR_CHATTER_GROUPS_MAP);
		/*Set<String> allOORGroups = new Set<String>();
		allOORGroups.addAll(OOR_RVP_CHATTER_GROUPS_MAP.values());
		allOORGroups.addAll(OOR_DIR_CHATTER_GROUPS_MAP.values());*/
		
		// OOR user chatter group for all sales
		for(String g : OOR_SALES_CHATTER_GROUPS)
		{
			chatterGroups.add(g);
		}
		
		// add user to the rvp/director if manager role matches
		/*String managerRole = u.Manager.UserRole.Name;
		if(oorManagerChatterGroups.contains(managerRole))   
		{
			chatterGroups.add(oorManagerRolesd.get(managerRole));
		}*/
		User mu = u;
		while(mu != null)
		{
			if(ALLUSERSMAP.containskey(mu.ManagerId))
			{
			   mu = ALLUSERSMAP.get(mu.ManagerId);
			   String managerRole = mu.UserRole.Name;
				if(allOORManagerGroupMap.containskey(managerRole))   
				{
					chatterGroups.add(allOORManagerGroupMap.get(managerRole));
				}
			}
			else
			{
			   mu = null;
			}
		}
		
		return chatterGroups;
	}
	
	/*
	   add user memberships to the mapped chatter group names, and only the group name is found in the provided group map
	*/
	/*private static void addUserToGroup(Map<Id,Set<String>> userGroupMap, Map<String, CollaborationGroup> allGroupsMap)
	{
		List<CollaborationGroupMember> members = new List<CollaborationGroupMember>();
		
		// loop through each user
		for(Id uId : userGroupMap.keyset())
		{
			// loop through each group the user should be joining
			for(String g : userGroupMap.get(uId))
			{
				// if the group name is found in the provided map
				if(allGroupsMap.containskey(g))
				{
					CollaborationGroup cg = allGroupsMap.get(g);
					
					if(uId != cg.OwnerId)
					{
					   CollaborationGroupMember m = new CollaborationGroupMember();
				      m.CollaborationGroupId = cg.Id;
					   m.MemberId = uId;
					   
					   members.add(m);
					}
				}
			}
		}
		
		if(!members.isEmpty())
		{
	      insert members;
		}		
	}*/
	
	/*
	   - add user memberships to the mapped chatter group names, and only the group name is found in the provided group map
	   - Remove user memeberships from input chatter group names, only if user is not the owner of the chatter group
	*/
	private static void calcUserMemberships(Map<Id,Set<String>> userGroupMap, Map<String, CollaborationGroup> allGroupsMap)
	{
		List<CollaborationGroupMember> membershipToAdd = new List<CollaborationGroupMember>();
		List<CollaborationGroupMember> membershipToRemove = new List<CollaborationGroupMember>();
		
		// loop through each user
		for(Id uId : userGroupMap.keyset())
		{
			// separate all groups into groups that users should join and groups users should not join
		   Set<String> groupMemRemove = new Set<String>();
		   Set<String> groupMemAdd = userGroupMap.get(uId);
		   
		   for(String g : allGroupsMap.keyset())
		   {
		   	if(!groupMemAdd.contains(g))
		   	   groupMemRemove.add(g);
		   }
		   
		   // go through all group and memberships for the group
		   for(String g : allGroupsMap.keyset())
		   {
		   	CollaborationGroup cg = allGroupsMap.get(g);
		   	
		   	// don't touch group if the user is the owner of the group
		   	if(uId != cg.OwnerId)
		   	{	
		   		// find user membership
		   		CollaborationGroupMember cgm = null;
		   		for(CollaborationGroupMember m : cg.GroupMembers)
		   		{
		   			if(m.MemberId == uId)
		   			{
		   			   cgm = m;
		   			   break;
		   			}
		   		}
		   		
		   	   // only add membership if the user is currently not in the group the user should be in
		   		if(cgm == null && groupMemAdd.contains(g))
		   		{
		   			CollaborationGroupMember m = new CollaborationGroupMember();
				      m.CollaborationGroupId = cg.Id;
					   m.MemberId = uId;
		   		   membershipToAdd.add(m);
		   		}
		   		else if(cgm != null && groupMemRemove.contains(g))
		   		{
		   			// remove membership if the user is in the group shouldn't be in
		   			membershipToRemove.add(cgm);
		   		}
		   	}
		   }	   	
		}
		
		if(!membershipToAdd.isEmpty())
		{
	      insert membershipToAdd;
		}		
		
		if(!membershipToRemove.isEmpty())
		{
			delete membershipToRemove;
		}
	}
	
	/*
	   Remove user memeberships from input chatter group names, only if user is not the owner of the chatter group
	*/
	/*private static void removeExisitngMemberships(Set<Id> uIds, Set<String> allGroupsNames)
	{	
		List<CollaborationGroupMember> cgmToDelete = new List<CollaborationGroupMember>();
		List<CollaborationGroupMember> cgm = [SELECT Id, CollaborationGroup.OwnerId, MemberId
		                                      FROM CollaborationGroupMember 
		                                      WHERE CollaborationGroup.Name IN :allGroupsNames AND
		                                            MemberId IN :uIds];
		
		for(CollaborationGroupMember m : cgm)
		{
			// delete all membership other than the owner
			if(m.MemberId != m.CollaborationGroup.OwnerId)
			   cgmToDelete.add(m);
		}
		                                            
		if(!cgmToDelete.isEmpty())
		   delete cgmToDelete;
	}*/
}